use sea_orm::{
    DatabaseConnection, DbErr, EntityTrait, QueryFilter, ColumnTrait,
    QuerySelect, sea_query::OnConflict, ActiveValue::Set
};
use crate::models::rbac::user_role::ActiveModel;
use crate::models::rbac::user_role::{Entity as UserRoleEntity, Column as UserRoleColumn};
use crate::models::rbac::{UserRole, RoleColumn};

pub struct UserRoleService;

impl UserRoleService {
    pub async fn assign_role_to_user(
        db: &DatabaseConnection,
        user_id: &str,
        role_id: &str,
        tenant_id: i64
    ) -> Result<u64, DbErr> {
        let mut model = ActiveModel {
            user_id: Set(user_id.to_string()),
            role_id: Set(role_id.to_string()),
            tenant_id: Set(Some(tenant_id)),
            ..Default::default()
        };

        UserRoleEntity::insert(model)
            .on_conflict(
                OnConflict::columns([
                    UserRoleColumn::UserId,
                    UserRoleColumn::RoleId,
                    UserRoleColumn::TenantId
                ])
                .do_nothing()
                .to_owned()
            )
            .exec(db)
            .await
            .map(|_| 1)
    }

    pub async fn remove_role_from_user(
        db: &DatabaseConnection,
        user_id: &str,
        role_id: &str,
        tenant_id: i64
    ) -> Result<u64, DbErr> {
        UserRoleEntity::delete_many()
            .filter(UserRoleColumn::UserId.eq(user_id))
            .filter(UserRoleColumn::RoleId.eq(role_id))
            .filter(UserRoleColumn::TenantId.eq(tenant_id))
            .exec(db)
            .await
            .map(|r| r.rows_affected)
    }

    pub async fn find_roles_by_user(
        db: &DatabaseConnection,
        user_id: &str,
        tenant_id: i64
    ) -> Result<Vec<String>, DbErr> {
        UserRoleEntity::find()
            .filter(UserRoleColumn::UserId.eq(user_id))
            .filter(UserRoleColumn::TenantId.eq(tenant_id))
            .inner_join(crate::models::rbac::RoleEntity)
            .select_only()
            .column(RoleColumn::Name)
            .into_tuple()
            .all(db)
            .await
    }

    pub async fn find_users_by_role(
        db: &DatabaseConnection,
        role_id: &str,
        tenant_id: i64
    ) -> Result<Vec<String>, DbErr> {
        UserRoleEntity::find()
            .filter(UserRoleColumn::RoleId.eq(role_id))
            .filter(UserRoleColumn::TenantId.eq(tenant_id))
            .select_only()
            .column(UserRoleColumn::UserId)
            .into_tuple()
            .all(db)
            .await
    }
    
    pub async fn find_by_id(
        db: &DatabaseConnection, 
        id: i64,
        tenant_id: i64
    ) -> Result<UserRole, DbErr> {
        UserRoleEntity::find_by_id(id)
            .filter(UserRoleColumn::TenantId.eq(tenant_id))
            .one(db)
            .await?
            .map(|model| UserRole {
                id: model.id,
                user_id: model.user_id,
                role_id: model.role_id,
                tenant_id: model.tenant_id,
                create_by: model.create_by,
                create_time: model.create_time,
                update_by: model.update_by,
                update_time: model.update_time,
            })
            .ok_or(DbErr::RecordNotFound(format!(
                "UserRole with id {} and tenant_id {} not found",
                id, tenant_id
            )))
    }
}
